/*
 * Copyright 2009 eBusinessInformation
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.excilys.ymca.task;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import roboguice.inject.ContextScoped;
import roboguice.inject.InjectResource;
import android.content.Context;
import android.os.AsyncTask;
import android.provider.Settings;
import android.util.Log;

import com.excilys.ymca.R;
import com.excilys.ymca.activity.ApplicationUpdateListener;
import com.google.inject.Inject;

/**
 * Tâche asynchrone de mise à jour de l'application.
 * 
 * @author Pierre-Yves Ricau ( py.ricau+ymca @ gmail.com )
 */
@ContextScoped
public class UpdateApplicationTask extends AsyncTask<Integer, Object, UpdateTaskResult> {

	private final ApplicationUpdateListener	listener;
	private final Context					context;

	@InjectResource(R.string.update_host)
	private String							host;

	@Inject
	public UpdateApplicationTask(ApplicationUpdateListener listener, Context context) {
		this.listener = listener;
		this.context = context;
	}

	@Override
	protected UpdateTaskResult doInBackground(Integer... params) {
		return updateApplication(params[0]);
	}

	@Override
	protected void onPostExecute(UpdateTaskResult result) {
		if (result.getException() != null) {
			listener.onUpdateFailed(result.getException());
		} else if (result.isUpToDate()) {
			listener.onApplicationUpToDate();
		} else {
			listener.onUpdateDownloaded(result.getDownloadedFile());
		}
	}

	private UpdateTaskResult updateApplication(int currentVersionCode) {
		try {
			int newVersionCode = retrieveNewVersionCode();

			if (newVersionCode <= currentVersionCode) {
				return UpdateTaskResult.upToDate();
			}

			int allowNonMarket = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.INSTALL_NON_MARKET_APPS, 0);
			if (allowNonMarket == 0) {
				throw new UnsupportedOperationException("Cannot install non-market apps. Please update your settings.");
			}

			String downloadedFileName = "YMCA-v" + newVersionCode + ".apk";

			downloadUpdate(newVersionCode, downloadedFileName);

			File downloadedFile = context.getFileStreamPath(downloadedFileName);

			return UpdateTaskResult.updateAvailable(downloadedFile);

		} catch (Exception e) {
			Log.d("UpdateApplicationTask", "An exception occured while updating application", e);
			return UpdateTaskResult.exception(e);
		}
	}

	/**
	 * Télécharge le .apk de la nouvelle version de l'application sur la carte
	 * SD.
	 */
	private void downloadUpdate(int newVersionCode, String downloadedFileName) throws IOException {
		InputStream is = getContentInputStream(host + "/YMCA-v" + newVersionCode + ".apk");

		OutputStream out = context.openFileOutput(downloadedFileName, Context.MODE_WORLD_READABLE);

		copy(is, out);

	}

	/**
	 * Récupère le code de version de la dernière version disponible
	 */
	private int retrieveNewVersionCode() throws IOException {
		String url = host + "/YMCA-version.txt";

		InputStream is = getContentInputStream(url);
		String versionCode = null;
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(is));
			versionCode = br.readLine();
		} finally {
			is.close();
		}
		return Integer.parseInt(versionCode);
	}

	/**
	 * Retourne un InputStream du contenu délivré par l'url fournnie en
	 * paramètre.
	 */
	private InputStream getContentInputStream(String url) throws IOException, ClientProtocolException {
		HttpParams params = new BasicHttpParams();

		HttpConnectionParams.setSoTimeout(params, 3000);
		HttpConnectionParams.setConnectionTimeout(params, 3000);

		DefaultHttpClient client = new DefaultHttpClient(params);

		HttpResponse response = client.execute(new HttpGet(url));

		int statusCode = response.getStatusLine().getStatusCode();
		if (statusCode != 200) {
			throw new RuntimeException("Response status code was not 200 but " + statusCode);
		}

		return response.getEntity().getContent();
	}

	/**
	 * Code extracted from Spring ;- Permet de copier un inputstream dans un
	 * outputstream
	 */
	private static int copy(InputStream in, OutputStream out) throws IOException {
		try {
			int byteCount = 0;
			byte[] buffer = new byte[1024];
			int bytesRead = -1;
			while ((bytesRead = in.read(buffer)) != -1) {
				out.write(buffer, 0, bytesRead);
				byteCount += bytesRead;
			}
			out.flush();
			return byteCount;
		} finally {
			try {
				in.close();
			} catch (IOException ex) {}
			try {
				out.close();
			} catch (IOException ex) {}
		}
	}

}
